From 2b2b55fcc25ddaba99b8fdaac151771ea1bcf658 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 17 Jan 2017 13:22:05 -0800 Subject: [PATCH] Add dep-info generation Make cargo output a ".d" file containing dependency info (in a format that make and ninja can consume) for each artifact it produces. This will help in integrating into other build systems. --- src/cargo/ops/cargo_rustc/mod.rs | 5 + src/cargo/ops/cargo_rustc/output_depinfo.rs | 127 ++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/cargo/ops/cargo_rustc/output_depinfo.rs diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 370b7e1dc..3fb4033ed 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -18,6 +18,8 @@ use util::Freshness; use self::job::{Job, Work}; use self::job_queue::JobQueue; +use self::output_depinfo::output_depinfo; + pub use self::compilation::Compilation; pub use self::context::{Context, Unit}; pub use self::custom_build::{BuildOutput, BuildMap, BuildScripts}; @@ -30,6 +32,7 @@ mod job; mod job_queue; mod layout; mod links; +mod output_depinfo; #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord)] pub enum Kind { Host, Target } @@ -184,6 +187,8 @@ pub fn compile_targets<'a, 'cfg: 'a>(ws: &Workspace<'cfg>, .or_insert(HashSet::new()) .extend(feats.iter().map(|feat| format!("feature=\"{}\"", feat))); } + + output_depinfo(&mut cx, unit)?; } for (&(ref pkg, _), output) in cx.build_state.outputs.lock().unwrap().iter() { diff --git a/src/cargo/ops/cargo_rustc/output_depinfo.rs b/src/cargo/ops/cargo_rustc/output_depinfo.rs new file mode 100644 index 000000000..a571d516c --- /dev/null +++ b/src/cargo/ops/cargo_rustc/output_depinfo.rs @@ -0,0 +1,127 @@ +use std::borrow::ToOwned; +use std::collections::HashSet; +use std::io::{Read, Write}; +use std::fs::File; +use std::path::{Path, PathBuf}; + +use core::{TargetKind}; +use ops::{Context, Unit}; +use util::{CargoResult, internal, human}; + +#[derive(Debug)] +struct DepLine { + target: String, + deps: Vec, +} + +struct DepFile { + dir: String, + deps: Vec, +} + +fn render_filename>(path: P, basedir: Option<&str>) -> CargoResult { + let path = path.as_ref(); + let relpath = match basedir { + None => path, + Some(base) => match path.strip_prefix(base) { + Ok(relpath) => relpath, + _ => path, + } + }; + relpath.to_str().ok_or(internal("path not utf-8")).map(ToOwned::to_owned) +} + +fn read_dep_file>(path: P) -> CargoResult { + let mut file = File::open(&path).map_err(|_| + human("error opening ".to_string() + path.as_ref().to_str().unwrap_or("(bad unicode")))?; + let mut contents = String::new(); + let _ = file.read_to_string(&mut contents)?; + let mut spl = contents.split('\0'); + let dir = spl.next().ok_or(internal("dependency file empty"))?; + let dep_txt = spl.next().ok_or(internal("dependency file missing null byte"))?; + let mut result = Vec::new(); + for line in dep_txt.lines() { + let mut line_spl = line.split(": "); + if let Some(target) = line_spl.next() { + if let Some(deps) = line_spl.next() { + let deps = deps.split_whitespace().map(ToOwned::to_owned).collect(); + result.push(DepLine { + target: target.to_string(), + deps: deps, + }); + } + } + } + Ok(DepFile { + dir: dir.to_string(), + deps: result, + }) +} + +fn add_deps(depfile: &DepFile, deps: &mut HashSet) { + let dep_dir = PathBuf::from(&depfile.dir); + for depline in &depfile.deps { + for dep in &depline.deps { + deps.insert(dep_dir.join(dep)); + } + } +} + +// TODO: probably better to use Context::target_filenames for this +fn target_filename(context: &mut Context, unit: &Unit) -> CargoResult { + let (dir, base) = context.link_stem(&unit).ok_or(internal("can't get link stem"))?; + if unit.target.is_lib() { + Ok(dir.join(["lib", &base, ".rlib"].concat())) + } else { + Ok(dir.join(base)) + } +} + +fn add_deps_for_unit(deps: &mut HashSet, context: &mut Context, unit: &Unit) + -> CargoResult<()> +{ + // TODO: this is duplicated against filename in fingerprint.rs + let kind = match *unit.target.kind() { + TargetKind::Lib(..) => "lib", + TargetKind::Bin => "bin", + TargetKind::Test => "integration-test", + TargetKind::Example => "example", + TargetKind::Bench => "bench", + TargetKind::CustomBuild => "build-script", + }; + let flavor = if unit.profile.test { + "test-" + } else if unit.profile.doc { + "doc-" + } else { + "" + }; + let dep_filename = ["dep-", flavor, kind, "-", &context.file_stem(&unit)].concat(); + let path = context.fingerprint_dir(&unit).join(&dep_filename); + let depfile = read_dep_file(&path)?; + add_deps(&depfile, deps); + Ok(()) +} + +pub fn output_depinfo(context: &mut Context, unit: &Unit) -> CargoResult<()> { + let mut deps = HashSet::new(); + add_deps_for_unit(&mut deps, context, unit)?; + for dep_unit in &context.dep_targets(unit)? { + let source_id = dep_unit.pkg.package_id().source_id(); + if source_id.is_path() { + add_deps_for_unit(&mut deps, context, dep_unit)?; + } + } + let filename = target_filename(context, unit)?; + let mut output_path = filename.clone().into_os_string(); + output_path.push(".d"); + let basedir = None; // TODO + let target_fn = render_filename(filename, basedir)?; + let mut outfile = File::create(output_path)?; + write!(outfile, "{}:", target_fn)?; + for dep in &deps { + write!(outfile, " {}", render_filename(dep, basedir)?)?; + } + writeln!(outfile, "")?; + Ok(()) +} -- 2.30.2